const crypto = require('crypto');
const axios = require('axios');

const {
    url,
    user,
    pwd,
} = require('/keys.js').zstack;

const client = axios.create({
    baseURL: url,
    timeout: 300 * 1000,
    headers: {
        'Content-Type':'application/json;charset=UTF-8',
    }
});

const session = {};

const login = () => {
    return new Promise((resolve) => {
        client.put('zstack/v1/accounts/login', {
            logInByAccount: {
                accountName: user,
                password: crypto.createHash('sha512').update(pwd).digest('hex'),
            },
        }).then((res) => {
            if (res.data.inventory) {
                console.log('zstack login success');
                session.uuid = res.data.inventory.uuid;
                session.expireTime = new Date(res.data.inventory.expiredDate);
                resolve(true);
            } else {
                console.error('zstack login: ', res.data);
                resolve(false);
            }
        }).catch((err) => {
            console.error('zstack login: ', err.message);
            resolve(false);
        });
    })
};

const ensureLogin = async () => {
    console.log('zstack ensure login', session.uuid, session.expireTime, session.expireTime < new Date(Date.now() + 1000 * 60 * 60));
    if (!session.uuid || (session.expireTime < new Date(Date.now() + 1000 * 60 * 60))) {
        return await login();
    }

    return true;
}

// 轮询一个异步借口的结果
// 通常情况下GET一个轮询地址可以得到四种返回：
// 1. 202返回码表示该API仍在处理中，用户需要继续轮询。
// 2. 200返回码表示API执行成功，Body中包含API结果。
// 3. 503返回码表示API执行失败，Body中包含错误码。
// 4. 404返回码，则表示轮询地址已经过期，产生这种结果的原因可能是用户访问了一个错误的轮询
// 地址，或者太久没有访问该轮询地址（例如超过2天没有访问），该轮询地址已经被删除。
const checkReqestResult = async (url) => {
    await ensureLogin();
    return new Promise((resolve) => {
        client.get(url, {
            headers: {
                Authorization: `OAuth ${session.uuid || ''}`,
            },
        }).then((res) => {
            if (res.status === 200) {
                resolve(res.data);
            } else {
                resolve();
            }
        }).catch((err) => {
            console.log('zstack check result: ', err.response.data.error.details);
            resolve('');
        });
    })
}

// 创建vm
const createVM = async (config = {}) => {
    if(!await ensureLogin()) return Promise.resolve();

    return new Promise((resolve) => {
        const vmName = `vm-${Date.now()}`;
        const randomPwd = crypto.randomBytes(32).toString('hex');
        let pwdCmdBase64 = `

            #cloud-config
            chpasswd:
              list: |
                  root:${randomPwd}
              expire: False`;
        pwdCmdBase64 = Buffer.from(pwdCmdBase64).toString('base64');

        const paramObj = {
          params: {
            name: vmName,
            description: "abc description",
            
            instanceOfferingUuid: config.instanceOfferingUuid,
            cpuNum: config.cpuNum || 1,
            memorySize: config.memorySize,

            rootDiskOfferingUuid: config.rootDiskOfferingUuid,
            rootDiskSize: config.rootDiskSize,

            dataDiskOfferingUuids: config.dataDiskOfferingUuids,
            dataDiskSizes: config.dataDiskSize ? [config.dataDiskSize] : [],

            imageUuid: config.imageUuid,

            l3NetworkUuids: [config.l3NetworkUuid],
            systemTags: [
                `directoryUuid::${config.directoryUuid}`,
                "resourceConfig::vm::vm.clock.track::guest",
                "vmPriority::Normal",
                `userdata::${pwdCmdBase64}`,
                `cdroms::${config.imageUuid}::None::None`,
                "vmConsoleMode::vnc",
                "cleanTraffic::false",
                `hostname::${vmName}`,
            ],
          },
          systemTags: [
            `directoryUuid::${config.directoryUuid}`,
            "resourceConfig::vm::vm.clock.track::guest",
            "vmPriority::Normal",
            `userdata::${pwdCmdBase64}`,
            `cdroms::${config.imageUuid}::None::None`,
            "vmConsoleMode::vnc",
            "cleanTraffic::false",
            `hostname::${vmName}`,
          ],
        };
        
        client.post('zstack/v1/vm-instances', paramObj, {
            headers: {
                Authorization: `OAuth ${session.uuid || ''}`,
            },
        }).then((res) => {
            const apiStartTime = Date.now();
            const checkTimer = setInterval(() => {
                // res.data.apiTimeout过了之后，结束轮询
                if (Date.now() - apiStartTime > res.data.apiTimeout) {
                    clearInterval(checkTimer);
                    resolve();
                }
                
                checkReqestResult(res.data.location).then((dd) => {
                    if (dd === '') {
                        clearInterval(checkTimer);
                        resolve();
                    }
                    if (dd) {
                        // return uuid of the vm and the ip address
                        resolve(dd.inventory ? {
                            uuid: dd.inventory.uuid,
                            ip: dd.inventory.vmNics[0].ip,
                            pwd: randomPwd,
                        } : undefined);

                        checkTimer && clearInterval(checkTimer);
                    }
                });
            }, 1000);
        }).catch((err) => {
            console.log('zstack create vm: ', err.message);
            resolve();
        });
    });
}

// 从快照创建vm
const createVMFromSnapshot = async (config = {}) => {
    if(!await ensureLogin()) return Promise.resolve();

    const {
        volumeSnapshotUuid,
        instanceOfferingUuid,
        hostUuid,
        primaryStorageUuid,
    } = config;

    // if (!volumeSnapshotUuid) return;

    return new Promise((resolve) => {
        const vmName = `vm-${Date.now()}`;
        const randomPwd = crypto.randomBytes(32).toString('hex');
        let pwdCmdBase64 = `

            #cloud-config
            chpasswd:
              list: |
                  root:${randomPwd}
              expire: False`;
        pwdCmdBase64 = Buffer.from(pwdCmdBase64).toString('base64');

        const paramObj = {
          params: {
            name: vmName,
            description: "abc description",
            volumeSnapshotUuid,
            hostUuid,
            instanceOfferingUuid,   
            l3NetworkUuids: [
                "b619e74b71524b4e971c40912fe24439"
            ],
            defaultL3NetworkUuid: "b619e74b71524b4e971c40912fe24439",
            primaryStorageUuid,
            systemTags: [
                `directoryUuid::${config.directoryUuid}`,
                "resourceConfig::vm::vm.clock.track::guest",
                "vmPriority::Normal",
                `userdata::${pwdCmdBase64}`,
                "vmConsoleMode::vnc",
                "cleanTraffic::false",
                `hostname::${vmName}`,
            ],
          },
          systemTags: [
            `directoryUuid::${config.directoryUuid}`,
            "resourceConfig::vm::vm.clock.track::guest",
            "vmPriority::Normal",
            `userdata::${pwdCmdBase64}`,
            "vmConsoleMode::vnc",
            "cleanTraffic::false",
            `hostname::${vmName}`,
          ],
        };
        
        client.post(`zstack/v1/vm-instances/from/volume-snapshots/${volumeSnapshotUuid}`, paramObj, {
            headers: {
                Authorization: `OAuth ${session.uuid || ''}`,
            },
        }).then((res) => {
            const apiStartTime = Date.now();
            const checkTimer = setInterval(() => {
                // res.data.apiTimeout过了之后，结束轮询
                if (Date.now() - apiStartTime > res.data.apiTimeout) {
                    clearInterval(checkTimer);
                    resolve();
                }
                
                checkReqestResult(res.data.location).then((dd) => {
                    if (dd === '') {
                        clearInterval(checkTimer);
                        resolve();
                    }
                    if (dd) {
                        // return uuid of the vm and the ip address
                        resolve(dd.inventory ? {
                            uuid: dd.inventory.uuid,
                            ip: dd.inventory.vmNics[0].ip,
                            pwd: randomPwd,
                        } : undefined);

                        checkTimer && clearInterval(checkTimer);
                    }
                });
            }, 1000);
        }).catch((err) => {
            console.log('zstack create vm: ', err.message);
            resolve();
        });
    });
};

// vm开机
const bootupVM = async (uuid) => {
    if(!await ensureLogin()) return Promise.resolve(false);

    return new Promise((resolve) => {        
        client.put(`zstack/v1/vm-instances/${uuid}/actions`, {
            startVmInstance: {},
        }, {
            headers: {
                Authorization: `OAuth ${session.uuid || ''}`,
            },
        }).then((res) => {
            const apiStartTime = Date.now();
            const checkTimer = setInterval(() => {
                // res.data.apiTimeout过了之后，结束轮询
                if (Date.now() - apiStartTime > res.data.apiTimeout) {
                    clearInterval(checkTimer);
                    resolve();
                }
                
                checkReqestResult(res.data.location).then((dd) => {
                    if (dd === '') {
                        clearInterval(checkTimer);
                        resolve();
                    }
                    if (dd) {
                        if (dd.error) {
                            console.log('zstack bootup vm: ', dd.error);
                            resolve(false);
                        }
                        
                        resolve(true);

                        checkTimer && clearInterval(checkTimer);
                    }
                });
            }, 1000);
        }).catch((err) => {
            console.log('zstack bootup vm: ', err.message);
            resolve();
        });
    });
};

// vm关机
const shutdownVM = async (uuid) => {
    if(!await ensureLogin()) return Promise.resolve(false);

    return new Promise((resolve) => {        
        client.put(`zstack/v1/vm-instances/${uuid}/actions`, {
            stopVmInstance: {
                type: 'grace', // grace, cold
            },
        }, {
            headers: {
                Authorization: `OAuth ${session.uuid || ''}`,
            },
        }).then((res) => {
            const apiStartTime = Date.now();
            const checkTimer = setInterval(() => {
                // res.data.apiTimeout过了之后，结束轮询
                if (Date.now() - apiStartTime > res.data.apiTimeout) {
                    clearInterval(checkTimer);
                    resolve();
                }
                
                checkReqestResult(res.data.location).then((dd) => {
                    if (dd === '') {
                        clearInterval(checkTimer);
                        resolve();
                    }
                    if (dd) {
                        if (dd.error) {
                            console.log('zstack shutdown vm: ', dd.error);
                            resolve(false);
                        }
                        
                        resolve(true);

                        checkTimer && clearInterval(checkTimer);
                    }
                });
            }, 1000);
        }).catch((err) => {
            console.log('zstack shutdown vm: ', err.message);
            resolve();
        });
    });
};

// 删除vm
const deleteVM = async (config = {}) => {
    await ensureLogin();
    // if(!await ensureLogin()) return;

    const {
        uuid,
    } = config;

    // if (!volumeSnapshotUuid) return;

    return new Promise((resolve) => {        
        client.delete(`zstack/v1/vm-instances/${uuid}`, {
            headers: {
                Authorization: `OAuth ${session.uuid || ''}`,
            },
        }).then((res) => {
            const apiStartTime = Date.now();
            const checkTimer = setInterval(() => {
                // res.data.apiTimeout过了之后，结束轮询
                if (Date.now() - apiStartTime > res.data.apiTimeout) {
                    clearInterval(checkTimer);
                    resolve();
                }
                
                checkReqestResult(res.data.location).then((dd) => {
                    if (dd === '') {
                        clearInterval(checkTimer);
                        resolve();
                    }
                    if (dd) {
                        if (dd.error) {
                            console.log('zstack delete vm: ', dd.error);
                            resolve(false);
                        }
                        
                        resolve(true);

                        checkTimer && clearInterval(checkTimer);
                    }
                });
            }, 1000);
        }).catch((err) => {
            console.log('zstack delete vm: ', err.message);
            resolve();
        });
    });
};

// 从快照恢复vm
const recoverVMFromSnapshot = async (uuid) => {
    await ensureLogin();
    // if(!await ensureLogin()) return;

    return new Promise((resolve) => {        
        client.put(`zstack/v1/volume-snapshots/${uuid}/actions`, {
            revertVolumeFromSnapshot: {},
        }, {
            headers: {
                Authorization: `OAuth ${session.uuid || ''}`,
            },
        }).then((res) => {
            const apiStartTime = Date.now();
            const checkTimer = setInterval(() => {
                // res.data.apiTimeout过了之后，结束轮询
                if (Date.now() - apiStartTime > res.data.apiTimeout) {
                    clearInterval(checkTimer);
                    resolve();
                }
                
                checkReqestResult(res.data.location).then((dd) => {
                    if (dd === '') {
                        clearInterval(checkTimer);
                        resolve();
                    }
                    if (dd) {
                        if (dd.error) {
                            console.log('zstack recover vm: ', dd.error);
                            resolve(false);
                        }
                        
                        resolve(true);

                        checkTimer && clearInterval(checkTimer);
                    }
                });
            }, 1000);
        }).catch((err) => {
            console.log('zstack recover vm: ', err.message);
            resolve();
        });
    });
};

module.exports = {
    createVM,
    createVMFromSnapshot,
    bootupVM,
    shutdownVM,
    deleteVM,
    recoverVMFromSnapshot,
};
